#! /usr/local/bin/perl
#
#  Copyright (C) 2003, OFFIS
#
#  This software and supporting documentation were developed by
#
#    Kuratorium OFFIS e.V.
#    Healthcare Information and Communication Systems
#    Escherweg 2
#    D-26121 Oldenburg, Germany
#
#  THIS SOFTWARE IS MADE AVAILABLE,  AS IS,  AND OFFIS MAKES NO  WARRANTY
#  REGARDING  THE  SOFTWARE,  ITS  PERFORMANCE,  ITS  MERCHANTABILITY  OR
#  FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES  OR
#  ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND
#  PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
#
#  Module:  config
#
#  Author:  Marco Eichelberg
#
#  Purpose: cleans up the Visual C++ project files (.dsp files) 
#  generated by CMake by exchanging absolute paths by relative paths and 
#  removing the custom build section that CMake creates for it's own 
#  makefiles.
#
#  This script expects that all external libraries (zlib, openssl etc.) 
#  are located in the same directory as the dcmtk base directory and 
#  that the output directory configured in CMake is identical to the 
#  input directory.
#
#  Last Update:      $Author: meichel $
#  Update Date:      $Date: 2003/09/10 14:12:27 $
#  Source File:      $Source: /share/dicom/cvs-depot/dcmtk/config/cleandsp.pl,v $
#  CVS/RCS Revision: $Revision: 1.1 $
#  Status:           $State: Exp $
#
#  CVS/RCS Log at end of file
#
#


# initialize global variables
$dspCount = 0;
undef @dspFiles;


# determineAbsolutePath(search_path: string)
#
# opens file ofstd/libsrc/ofstd.dsp and locates the
# absolute path to the DCMTK base directory as written by CMake.
# @param search_path path to the DCMTK base directory as seen from the Perl script
# @return absolute path to the DCMTK base directory as written by CMake
#
sub determineAbsolutePath
{
  local($startpath) = @_;
  local($filename) = "$startpath/ofstd/libsrc/ofstd.dsp";
  if (!open(INFILE,"<$filename")) { die "sorry, did not find file '$filename'"; }
  while (<INFILE>)
  {
    if ($_ =~ /\/I ".*[\\\/]ofstd[\\\/]include"/)
    {
      local($path) = ($_ =~ /\/I "([^"]*[\\\/])ofstd[\\\/]include"/);
      close(INFILE);
      return $path;
    }
  }
  close(INFILE);
  die "sorry, could not find include path in .dsp file";
}

# locateDspFiles(search_path: string)
#
# locates all ".dsp" files in the search path
# @param search_path path to the DCMTK base directory as seen from the Perl script
# @return function result is stored in the global variables $dspCount and @dspFiles.
#
sub locateDspFiles
{
  local($startpath) = @_;
  opendir(DIR, $startpath) || die "can't opendir $startpath: $!";
  local(@files) = readdir(DIR);
  closedir(DIR);

  foreach $_ (@files)
  {
    if (($_ =~ /^[^\.]/) && -d "$startpath/$_")
    {
      # recurse into subdirectory
      &locateDspFiles("$startpath/$_");
    }
    if (($_ =~ /\.dsp$/) && -f "$startpath/$_")
    {
      $dspFiles[$dspCount++] = "$startpath/$_";
    }
  }
}

# replaceBackslashes(s: string)
#
# replace all backslash characters in string by forward slashes
# @return modified string
sub replaceBackslashes
{
  local($result) = @_;
  $result =~ s/\\/\//g;
  $result;
}

# computeRelativePath(startpath, dspfile: string)
# compute a relative path to the startpath from the location
# of the dspfile. 
# @param startpath path to the DCMTK base directory as seen from the Perl script
# @param dspfile path to .dsp file as seen from the Perl script
# @return relative path from location of .dsp file to DCMTK base directory
#
sub computeRelativePath
{
  local($startpath, $dspfile) = @_;
  $startpath = &replaceBackslashes($startpath);
  $dspfile = &replaceBackslashes($dspfile);
  if (substr($dspfile, 0, length($startpath)) ne $startpath)
  {
    die "error: path '$dspfile' does not start with '$startpath'";
  }

  local($path_from_startpath) = substr($dspfile, length($startpath));
  local($path) = ($path_from_startpath =~ /^(.*)\/[a-z0-9_]+\.dsp$/i);

  # count slashes in $path
  local($count) = 0;
  local($index) = 0;
  do
  {
    $index = index($path, "/");
    if ($index >= 0)
    {
      $count++;
      $path = substr($path, $index + 1);
    }
  } while ($index >= 0);

  local($result);  
  local($i);
  for ($i=0; $i < $count; $i++)
  {
    $result = "../$result";
  }

  # return relative path
  $result;
}

# computeAbsoluteLibPath(libpath: string)
# compute the absolute path to external libraries from the
# absolute path to the DCMTK base directory
# @param libpath absolute path to DCMTK base directory in .dsp file, as written by CMake
# @return absolute path to base directory for external libraries
#
sub computeAbsoluteLibPath
{
  local($path) = @_;
  ($path) = ($path =~ /^(.*[\\\/])[^\\\/]+[\\\/]/);
  return $path;
}

# computeRelativeLibPath(libpath: string)
# compute the relative path to external libraries from the
# relative path to the DCMTK base directory
# @param libpath relative path to DCMTK base directory in .dsp file
# @return relative path to base directory for external libraries
#
sub computeRelativeLibPath
{
  local($path) = @_;
  $path = "../$path";
  return $path;
}

# fixDspFile(filename, absolutepath, relativepath: string)
# 
# open .dsp file and replace all occurences of $absolutepath with $relativepath.
# This routine creates a temporary file which is finally renamed to the original filename.
# @param filename path to .dsp file as seen from perl script
# @param absolutepath absolute path in .dsp file, as written by CMake
# @param relativepath relative path for .dsp file
#
sub fixDspFile
{
  local($filename, $absolutepath, $relativepath) = @_;

  if (!open(INFILE,"<$filename")) { die "sorry, did not find file '$filename'"; }
  if (!open(OUTFILE,">$filename.\$\$\$")) { die "sorry, unable to create file '$filename.\$\$\$'"; }
  local($index);
  local($len) = length($absolutepath);
  while (<INFILE>)
  {
    do
    {
      $index = index($_, $absolutepath);
      if ($index >= 0)
      {
        substr($_, $index, $len) = $relativepath;
      }      
    } while ($index >= 0);

    print OUTFILE $_;
  }
  close(OUTFILE);
  close(INFILE);
  if (!rename("$filename.\$\$\$","$filename")) { die "sorry, unable to move file '$filename.\$\$\$' to '$filename'"; }  
}

# removeCustomBuildSection(filename: string)
# 
# open .dsp file and remove any custom build section for a file named CMakeLists.txt.
# This routine creates a temporary file which is finally renamed to the original filename.
# @param filename path to .dsp file as seen from perl script
#
sub removeCustomBuildSection
{
  local($filename) = @_;
  local($line1, $line2);

  if (!open(INFILE,"<$filename")) { die "sorry, did not find file '$filename'"; }
  if (!open(OUTFILE,">$filename.\$\$\$")) { die "sorry, unable to create file '$filename.\$\$\$'"; }
  while (<INFILE>)
  {
    # check for "Begin Source File" statement.
    if ($_ =~ /^# Begin Source File/)
    {
      # read next two lines
      $line1 = <INFILE>;
      $line2 = <INFILE>;
      # check if this is a custum build section for CMake. [\015]? handles CR before LF on Unix systems.
      if ($line2 =~ /^SOURCE=.*\\CMakeLists\.txt[\015]?$/)
      {
        # found a custom build section. Skip all lines until (including) the "End Source File" line.
        do { $line1 = <INFILE>; } while (! ($line1 =~ /^# End Source File/));
      }
      else
      {
        # file name did not match, just copy input to output file
        print OUTFILE $_, $line1, $line2;
      }  
    }
    else
    {
      print OUTFILE $_;
    }  
  }
  close(OUTFILE);
  close(INFILE);
  if (!rename("$filename.\$\$\$","$filename")) { die "sorry, unable to move file '$filename.\$\$\$' to '$filename'"; }  
}

#
# MAIN BEGINS HERE
#

select(STDERR); $| = 1;     # make unbuffered
select(STDOUT); $| = 1;     # make unbuffered

if ($#ARGV <0)
{
  print <<ENDL;

usage: cleandsp.pl <dcmtk_base_directory>

  cleans up the Visual C++ project files (.dsp files) generated by CMake 
  by exchanging absolute paths by relative paths and removing the custom
  build section that CMake creates for it's own makefiles.

  This script expects that all external libraries (zlib, openssl etc.)
  are located in the same directory as the dcmtk base directory
  and that the output directory configured in CMake is identical to
  the input directory.

ENDL
  exit 0;
}

$dcmtk_base_directory = $ARGV[0];
if (! -d $dcmtk_base_directory)
{
  die "sorry, '$dcmtk_base_directory' not found or not a directory";
}

$absolutepath = &determineAbsolutePath($dcmtk_base_directory);

# make sure we have really found an absolute path, beginning with a drive letter
if (! ($absolutepath =~ /^[a-zA-Z]:/))
{
  print "no absolute path found in .dsp files - are the files already cleaned?\n";
  exit 0;
}

$absolutelibpath = &computeAbsoluteLibPath($absolutepath);

print "--------------------------------------------------------------------\n",
      "Cleaning up Visual C++ project files (.dsp files) generated by CMake\n",
      "--------------------------------------------------------------------\n\n";
printf("Absolute DCMTK base path in project files is '$absolutepath'\n");
printf("Working: [");

&locateDspFiles($dcmtk_base_directory);
if ($dspCount == 0)
{
  die "sorry, no .dsp files found in '$dcmtk_base_directory'";
}

# iterate over all .dsp files
for ($i=0; $i<$dspCount; $i++)
{
  $relativepath = &computeRelativePath($dcmtk_base_directory, $dspFiles[$i]);
  $relativelibpath = &computeRelativeLibPath($relativepath);

  # convert DCMTK paths to relative paths
  &fixDspFile($dspFiles[$i], $absolutepath, $relativepath);
  # convert external library paths to relative paths
  &fixDspFile($dspFiles[$i], $absolutelibpath, $relativelibpath);
  # remove custom build sections for CMakeLists.txt
  &removeCustomBuildSection($dspFiles[$i]);
  # print a dot for each 10 files
  if ($i % 10 == 0) { print "."; }
}
printf("] done.\n\nNumber of project files cleaned: %i\n\n", $dspCount);

# done
exit 0;

#
# $Log: cleandsp.pl,v $
# Revision 1.1  2003/09/10 14:12:27  meichel
# Initial release of Perl script "cleandsp.pl" which cleans up the
#   Visual C++ project files (.dsp files) generated by CMake by exchanging
#   absolute paths by relative paths and removing the custom build section
#   that CMake creates for it's own makefiles.
#
#
